package massGP;

import firing.FiringTree;
import gp.EliteSelection;
import gp.GreedyOverSelection;
import gp.HarbingerOfHeuristics;
import gp.RouletteWheelSelection;
import gp.SelectionStrategy;
import gp.TournamentSelection;
import gp.TreeGenerator;
import gp.TruncationSelection;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class GPGui {
    
    List<FiringTree> population;
    SelectionStrategy[] strategies = {new RouletteWheelSelection(),new GreedyOverSelection(),new TournamentSelection(),new TruncationSelection(), new EliteSelection()};
    int currentGeneration = 1;
    LinkedList<Component> disableableButtons;
    
    JFrame app;
    JPanel mutateWindow;
    JPanel fileButtons;
    JPanel status;
    JPanel mutation;
    JPanel individualView;
    JPanel individualViewButtons;
    
    JLabel populationLabel;
    JLabel populationStatus;
    
    JButton load;
    JButton save;
    JButton newPop;
    JButton launch;
    
    JLabel stratLabel;
    JComboBox<SelectionStrategy> selectionStrategy;
    JLabel generationLabel;
    JTextField generations;
    JLabel sizeLabel;
    JTextField size;
    JButton mutate;
    JProgressBar progressBar;
    
    JTextArea treeView;
    JButton launchIndividual;
    JComboBox<FiringTree> trees;
    
    public GPGui() {
	disableableButtons = new LinkedList<Component>();
	population = new ArrayList<FiringTree>();
	
	app = new JFrame("Genetic Programming Iteration");
	mutateWindow = new JPanel();
	app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	mutateWindow.setPreferredSize(new Dimension(500,200));
	fileButtons = new JPanel();
	JPanel status = new JPanel();
	JPanel mutation = new JPanel();
	
	status.setLayout(new BoxLayout(status, BoxLayout.X_AXIS));
	//status.setSize(new Dimension(360,50));
	populationLabel = new JLabel("Current Population: ");
	populationStatus = new JLabel("None");
	status.add(populationLabel);
	status.add(populationStatus);	
	
	fileButtons.setLayout(new GridLayout(4,1,10,10));
	//fileButtons.setSize(new Dimension(120,150));
	load = new JButton("Load");
	save = new JButton("Save");
	newPop = new JButton("New Population");
	launch = new JButton("Launch Game");
	fileButtons.add(load);
	fileButtons.add(save);
	fileButtons.add(newPop);
	fileButtons.add(launch);
	
	mutation.setLayout(new GridLayout(4,2));
	//mutation.setPreferredSize(new Dimension(300,600));
	stratLabel = new JLabel("Selection Strategy");
	selectionStrategy = new JComboBox<SelectionStrategy>(strategies);
	generationLabel = new JLabel("Generations:");
	generations = new JTextField("10");
	mutate = new JButton("Mutate!");
	progressBar = new JProgressBar(0,100);
	sizeLabel = new JLabel("Population size");
	size = new JTextField("1000");
	mutation.add(stratLabel);
	mutation.add(selectionStrategy);
	mutation.add(generationLabel);
	mutation.add(generations);
	mutation.add(sizeLabel);
	mutation.add(size);
	mutation.add(mutate);
	mutation.add(progressBar);
	mutate.setEnabled(false);
	save.setEnabled(false);
	
	disableableButtons.add(mutate);
	disableableButtons.add(selectionStrategy);
	disableableButtons.add(generations);
	disableableButtons.add(newPop);
	disableableButtons.add(save);
	disableableButtons.add(load);
	//disableableButtons.add(launch);
	disableableButtons.add(size);
	
	mutateWindow.setLayout(new BorderLayout());
	mutateWindow.add(status,BorderLayout.NORTH);
	mutateWindow.add(fileButtons, BorderLayout.WEST);
	mutateWindow.add(mutation, BorderLayout.EAST);
	mutateWindow.setVisible(true);
	
	individualView = new JPanel();
	individualViewButtons = new JPanel();
	launchIndividual = new JButton("Launch");
	trees = new JComboBox<FiringTree>();
	trees.setModel(new DefaultComboBoxModel(population.toArray()));
	treeView = new JTextArea();
	treeView.setEditable(false);
	individualViewButtons.setLayout(new GridLayout(1,2));
	individualView.setLayout(new BorderLayout());
	individualViewButtons.add(trees);
	individualViewButtons.add(trees);
	individualViewButtons.add(launchIndividual);
	JScrollPane treeScroll = new JScrollPane();
	treeScroll.add(treeView);
	//treeScroll.setPreferredSize(new Dimension(450,300));
	individualView.add(individualViewButtons,BorderLayout.NORTH);
	individualView.add(treeView,BorderLayout.CENTER);
	
	setUpListeners();
	JTabbedPane tabs = new JTabbedPane();
	tabs.add("mutation",mutateWindow);
	tabs.add("individual view",individualView);
	app.add(tabs);
	app.pack();
	app.setVisible(true);
    }
    private void setUpListeners() {
	newPop.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		population.clear();
		currentGeneration = 1;
		populationStatus.setText("New Population("+ size.getText() + ")");
		for(int i=0;i<Integer.parseInt(size.getText());i++) {
		    population.add(TreeGenerator.generateTree());
		}
		mutate.setEnabled(true);
		save.setEnabled(true);
		trees.setModel(new DefaultComboBoxModel(population.toArray()));
	    }
	});
	
	load.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		final JFileChooser fc = new JFileChooser();
		fc.setCurrentDirectory(new File("MassTrees"));
		fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		int result = fc.showOpenDialog(mutateWindow);
		if (result == JFileChooser.APPROVE_OPTION) {
		    try {
			File[] files = fc.getSelectedFile().listFiles(new FilenameFilter() {
			    public boolean accept(File dir, String name) {
				return name.toLowerCase().endsWith(".ftree");
			    }
			});
			for(File f:files) {
			    FileInputStream fileIn = new FileInputStream(f);
			    ObjectInputStream in = new ObjectInputStream(fileIn);
			    population.add((FiringTree) in.readObject());
			    in.close();
			    fileIn.close();
			}
			
		    } catch(IOException ex) {
			ex.printStackTrace();
		    } catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		    }
		    populationStatus.setText("Loaded Population("+ population.size() + ")");
		    save.setEnabled(true);
		    mutate.setEnabled(true);
		    trees.setModel(new DefaultComboBoxModel(population.toArray()));
		    currentGeneration = population.get(0).getGeneration();
		}
	    }
	});
	
	save.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		final JFileChooser fc = new JFileChooser();
		fc.setCurrentDirectory(new File("MassTrees"));
		fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		int result = fc.showSaveDialog(mutateWindow);
		if (result == JFileChooser.APPROVE_OPTION) {
		    for(int i=0;i<population.size();i++) {
			try {
			    File target = new File(fc.getSelectedFile().getAbsolutePath() + "/" + population.get(i).toString(i));
			    FileOutputStream fileOut = new FileOutputStream(target);
			    ObjectOutputStream out = new ObjectOutputStream(fileOut);
			    out.writeObject(population.get(i));
			    out.close();
			    fileOut.close();
			} catch(IOException ex) {
			    ex.printStackTrace();
			}
		    }
		}
	    }
	});
	
	mutate.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		new Thread(new MutateAction()).start();
	    }
	});
	launch.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		new gui.GameGui();
	    }
	});
	launchIndividual.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		new gui.GameGui((FiringTree) trees.getSelectedItem());
	    }
	});
	trees.addActionListener(new ActionListener() {
	    public void actionPerformed(ActionEvent e) {
		treeView.setText(((FiringTree)(trees.getSelectedItem())).printTree());
	    }
	});
	
    }
    public static void main(String[] args) {
	new GPGui();
    }
    
    class MutateAction implements Runnable {

	@Override
	public void run() {
	    mutate.setEnabled(false);
	    for(Component c:disableableButtons) {
		c.setEnabled(false);
	    }
	    
	    progressBar.setValue(0);
	    progressBar.setStringPainted(true);
	    for(int i=0;i<Integer.parseInt(generations.getText());i++) {
		HarbingerOfHeuristics.getHeuristic(population);
		List<FiringTree> temp = population;
		temp = strategies[selectionStrategy.getSelectedIndex()].getNextGen(population);
		population = temp;
		progressBar.setValue((i+1)*100 / (Integer.parseInt(generations.getText())+1));
		populationStatus.setText("Generation " + currentGeneration);
		currentGeneration++;
	    }
	    HarbingerOfHeuristics.getHeuristic(population);
	    progressBar.setValue(0);
	    progressBar.setStringPainted(false);
	    for(Component c:disableableButtons) {
		c.setEnabled(true);
	    }
	    trees.setModel(new DefaultComboBoxModel(population.toArray()));
	}
	
    }
    
}
